package org.neogenesis.web.loader.geoDataseLoader;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.neogenesis.web.loader.MetaPlatform;
import org.neogenesis.web.loader.MetaSample;
import org.neogenesis.web.loader.ProbesLoader;
import org.neogenesis.web.loader.ProbesLoaderGeoSeries;
import org.neogenesis.web.model.Experiment;
import org.neogenesis.web.model.Organism;
import org.neogenesis.web.model.Platform;
import org.neogenesis.web.model.TypePlatform;
import org.neogenesis.web.schema.AccessionType;
import org.neogenesis.web.schema.ChannelType;
import org.neogenesis.web.schema.CharacteristicsType;
import org.neogenesis.web.schema.MINiML;
import org.neogenesis.web.schema.OrganismType;
import org.neogenesis.web.schema.RefType;
import org.neogenesis.web.schema.Sample;
import org.neogenesis.web.schema.SampleType;
import org.neogenesis.web.schema.Series;
import org.neogenesis.web.schema.SupplementLinkType;
import org.neogenesis.web.schema.TableType;
import org.neogenesis.web.schema.TechnologyType;

public class MINimlLoader {

	private String folderPath;

	private String fileName;

	private MINiML miNiML;

	private List<org.neogenesis.web.schema.Platform> mPlatforms;

	private List<org.neogenesis.web.schema.Sample> mListSamples;

	private Series series;

	private Map<String, MetaPlatform> platforms;

	private List<MetaSample> metaSamples;

	private Experiment experiment = null;

	public static MINiML unmarshalXml(String path) throws JAXBException {

		JAXBContext jaxbContext = JAXBContext.newInstance(MINiML.class);
		Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
		MINiML miNiML = (MINiML) unmarshaller.unmarshal(new File(path));

		return miNiML;

	}

	public MINimlLoader(String folderPath, String fileName)
			throws JAXBException {

		this.folderPath = folderPath;
		this.fileName = fileName;

		if (!this.folderPath.endsWith("/"))
			this.folderPath += "/";

		String path = this.folderPath + fileName;

		miNiML = unmarshalXml(path);

		List<Object> list = miNiML.getPlatformOrSampleOrSeries();
		mPlatforms = new ArrayList<org.neogenesis.web.schema.Platform>();

		mListSamples = new ArrayList<org.neogenesis.web.schema.Sample>();

		for (Object o : list) {
			if (o instanceof org.neogenesis.web.schema.Platform) {
				mPlatforms.add((org.neogenesis.web.schema.Platform) o);
			}

			if (o instanceof org.neogenesis.web.schema.Series) {
				series = (org.neogenesis.web.schema.Series) o;
			}

			if (o instanceof org.neogenesis.web.schema.Sample) {
				mListSamples.add((org.neogenesis.web.schema.Sample) o);
			}
		}
	}

	public Map<String, MetaPlatform> getPlatforms() {
		if (platforms != null)
			return platforms;

		platforms = new HashMap<String, MetaPlatform>();

		Platform platform;
		for (org.neogenesis.web.schema.Platform pl : mPlatforms) {
			// get the platform from the file
			List<Object> list = pl.getContent();
			JAXBElement elem;
			String name;

			platform = new Platform();
			OrganismType organismType = null;
			TechnologyType technologyType = null;

			for (int i = 0; i < list.size(); i++) {
				elem = (JAXBElement) list.get(i);
				name = elem.getName().getLocalPart();

				if (name.equalsIgnoreCase("Title")) {
					platform.setTitle(elem.getValue().toString());
				} else if (name.equalsIgnoreCase("Accession")) {
					AccessionType accessionType = (AccessionType) elem
							.getValue();
					platform.setAccessionName(accessionType.getValue());
				} else if (name.equalsIgnoreCase("Description")) {
					platform.setDescription(elem.getValue().toString());
				} else if (name.equalsIgnoreCase("Manufacturer")) {
					platform.setManufacturer(elem.getValue().toString());
				} else if (name.equalsIgnoreCase("Organism")) {
					organismType = (OrganismType) elem.getValue();
				} else if (name.equalsIgnoreCase("Technology")) {
					technologyType = (TechnologyType) elem.getValue();
				}
			}

			platform.getOrganism().setName(organismType.getValue());
			platform.getTypePlatform().setName(technologyType.toString());

			MetaPlatform metaPlatform = new MetaPlatform();
			metaPlatform.setPlatform(platform);

			platforms.put(platform.getAccessionName(), metaPlatform);
		}

		return platforms;
	}

	public Organism getOrganism(String platformName) {
		getPlatforms();

		return platforms.get(platformName).getPlatform().getOrganism();
	}

	public TypePlatform getTypePlatform(String platformName) {
		getPlatforms();

		return platforms.get(platformName).getPlatform().getTypePlatform();
	}

	public Experiment getExperiment() {
		if (experiment != null)
			return experiment;

		experiment = new Experiment();

		experiment.setSeriesIid(series.getIid());
		List<Object> elements = series.getContent();

		for (Object o : elements) {
			JAXBElement e = (JAXBElement) o;
			String name = e.getName().getLocalPart();

			if (name.equalsIgnoreCase("Title")) {
				experiment.setTitle(e.getValue().toString());
			} else if (name.equalsIgnoreCase("Pubmed-ID")) {
				experiment.setPubmedId(e.getValue().toString());
			} else if (name.equalsIgnoreCase("Summary")) {
				experiment.setSummary(e.getValue().toString());
			} else if (name.equalsIgnoreCase("Overall-Design")) {
				experiment.setOverallDesign(e.getValue().toString());
			} else if (name.equalsIgnoreCase("Overall-Design")) {
				experiment.setOverallDesign(e.getValue().toString());
			} else if (name.equalsIgnoreCase("Type")) {
				experiment.setType(e.getValue().toString());
			}
		}
		return experiment;

	}

	public ProbesLoader getProbesLoader(String platformName) throws IOException {

		if (platforms != null) {
			MetaPlatform metaPlatform = platforms.get(platformName);
			if (metaPlatform != null) {
				if (metaPlatform.getProbesLoader() != null)
					return metaPlatform.getProbesLoader();
			}
		}

		MetaPlatform metaPlatform = platforms.get(platformName);

		// get the data table
		TableType tableType = loadTableType(platformName);

		// get the external file
		String probesFile = tableType.getExternalData().getValue();

		// load the metaProbes from the file
		ProbesLoader probesLoader = new ProbesLoaderGeoSeries(folderPath + probesFile,
				tableType.getColumn());

		platforms.get(platformName).setType(probesLoader.getTypeMetaPlatform());
		
		metaPlatform.setProbesLoader(probesLoader);

		return probesLoader;
	}

	private TableType loadTableType(String platformName) {

		TableType tableType = null;

		org.neogenesis.web.schema.Platform mPlatform = getGeoPlatform(platformName);

		List<Object> list = mPlatform.getContent();
		JAXBElement elem;
		String name;

		for (int i = 0; i < list.size(); i++) {
			elem = (JAXBElement) list.get(i);
			name = elem.getName().getLocalPart();

			if (name.equalsIgnoreCase("Data-Table")) {
				tableType = (TableType) elem.getValue();
				break;
			}
		}

		return tableType;
	}

	private org.neogenesis.web.schema.Platform getGeoPlatform(
			String platformName) {
		for (org.neogenesis.web.schema.Platform pl : mPlatforms) {
			List<Object> list = pl.getContent();
			JAXBElement elem;
			String name;

			for (int i = 0; i < list.size(); i++) {
				elem = (JAXBElement) list.get(i);
				name = elem.getName().getLocalPart();

				if (name.equalsIgnoreCase("Accession")) {
					AccessionType accessionType = (AccessionType) elem
							.getValue();
					if (accessionType.getValue().equalsIgnoreCase(platformName))
						return pl;
				}
			}
		}
		return null;
	}

	public List<MetaSample> getSamples() {

		if (metaSamples != null)
			return metaSamples;

		metaSamples = new ArrayList<MetaSample>(mListSamples.size());

		GeoMetaSample mSample;

		for (Sample s : mListSamples) {
			mSample = new GeoMetaSample(folderPath + s.getIid());
			// set the name
			mSample.setName(s.getIid());
			
			List<Object> elements = s.getContent();

			for (Object o : elements) {
				JAXBElement e = (JAXBElement) o;
				String name = e.getName().getLocalPart();
				

				if (name.equalsIgnoreCase("Title")) {
					mSample.getSample().setTitle(e.getValue().toString());
				} else if (name.equalsIgnoreCase("Channel")) {
					ChannelType channel = (ChannelType) e.getValue();
					mSample.getSample().setTreatmentProtocol(
							channel.getTreatmentProtocol());
				} else if (name.equalsIgnoreCase("Description")) {
					mSample.getSample().setDescription(e.getValue().toString());
				} else if (name.equalsIgnoreCase("Data-Processing")) {
					mSample.getSample().setDataPreprocessing(
							e.getValue().toString());
				} else if (name.equalsIgnoreCase("Supplementary-Data")) {
					SupplementLinkType supplementary = (SupplementLinkType) e
							.getValue();
					mSample.getSample().setSupplementaryDataFile(
							supplementary.getValue());
				} else if (name.equalsIgnoreCase("Type")) {
					mSample.setType((SampleType) e.getValue());
				} else if (name.equalsIgnoreCase("Platform-Ref")) {
					RefType refType = (RefType) e.getValue();
					org.neogenesis.web.schema.Platform platform = (org.neogenesis.web.schema.Platform) refType
							.getRef();
					mSample.setPlatformNameRef(platform.getIid());
				}else if (name.equals("Channel")){
					ChannelType channel = (ChannelType) e.getValue();
					List<CharacteristicsType> chs =  channel.getCharacteristics();
					for (CharacteristicsType ch : chs) {
						
					}
				}
			}
			metaSamples.add(mSample);
		}

		return metaSamples;
	}

	public static void main(String[] args) {
		String folderPath = "GSE23603_family.xml";
		String fileName = "GSE23603_family.xml";
		try {
			MINimlLoader nimlLoader = new MINimlLoader(folderPath, fileName);

			Map<String, MetaPlatform> platforms = nimlLoader.getPlatforms();

			for (String key : platforms.keySet()) {

				Organism organism = nimlLoader.getOrganism(key);

				TypePlatform typePlatform = nimlLoader.getTypePlatform(key);

				List<MetaSample> samples = nimlLoader.getSamples();

				for (MetaSample metaSample : samples) {
					List<String[]> observations = metaSample.getObservations();
					// for (String[] value : observations) {
					// System.out.println(Arrays.toString(value));
					// }
					System.out.println(metaSample.getSample().getName());
					System.out.println(metaSample.getPlatformNameRef());
				}
			}

		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
